---
title: Retry Plugin
description: Automatic retry for resource loading failures in Module Federation. Supports domain rotation, cache-busting, and custom callbacks.
---

## Retry Plugin

The Retry Plugin provides a robust retry mechanism for Module Federation. When remote modules or resources fail to load, it retries automatically to keep your app stable.

## Features

- **Automatic retries**: Improve stability by retrying failed resource loads
- **Domain rotation**: Switch across multiple backup domains automatically
- **Cache-busting**: Add query parameters to avoid cache interference on retries
- **Flexible config**: Customize retry times, delay, and callbacks

## Install

```bash
npm install @module-federation/retry-plugin
```

## Migration Guide

### From v0.18.x to v0.19.x

The plugin configuration has been simplified. The old `fetch` and `script` configuration objects are deprecated:

```ts
// ❌ Old way (deprecated)
RetryPlugin({
  fetch: {
    url: 'http://localhost:2008/not-exist-mf-manifest.json',
    fallback: () => 'http://localhost:2001/mf-manifest.json',
  },
  script: {
    url: 'http://localhost:2001/static/js/async/src_App_tsx.js',
    customCreateScript: (url, attrs) => { /* ... */ },
  }
})

// ✅ New way
RetryPlugin({
  retryTimes: 3,
  retryDelay: 1000,
  domains: ['http://localhost:2001'],
  manifestDomains: ['http://localhost:2001'],
  addQuery: ({ times, originalQuery }) => `${originalQuery}&retry=${times}`,
})
```

## Usage
### Method1: Used in the build plugin

```ts title="rspack.config.ts"
import { pluginModuleFederation } from '@module-federation/rsbuild-plugin';
import { defineConfig } from '@rsbuild/core';

export default defineConfig({
  plugins: [
    pluginReact(),
    pluginModuleFederation({
      runtimePlugins: [
        path.join(__dirname, './src/runtime-plugin/retry.ts'),
      ],
    }),
  ],
});

```

```ts
// ./src/runtime-plugin/retry.ts
import { RetryPlugin } from '@module-federation/retry-plugin';
const retryPlugin = () => RetryPlugin({
  retryTimes: 3,
  retryDelay: 1000,
  manifestDomains: ['https://domain1.example.com', 'https://domain2.example.com'],
  domains: ['https://cdn1.example.com', 'https://cdn2.example.com'],
  addQuery: ({ times, originalQuery }) => `${originalQuery}&retry=${times}`,
  onRetry: ({ times, url }) => console.log('retry', times, url),
  onSuccess: ({ url }) => console.log('success', url),
  onError: ({ url }) => console.log('error', url),
})
export default retryPlugin;
```

### Method2: Used in the pure runtime

```ts
import { createInstance, loadRemote } from '@module-federation/enhanced/runtime';
import { RetryPlugin } from '@module-federation/retry-plugin';

const mf = createInstance({
  name: 'federation_consumer',
  remotes: [],
  plugins: [
    RetryPlugin({
      retryTimes: 3,
      retryDelay: 1000,
      manifestDomains: ['https://domain1.example.com', 'https://domain2.example.com'],
      domains: ['https://cdn1.example.com', 'https://cdn2.example.com'],
      addQuery: ({ times, originalQuery }) => `${originalQuery}&retry=${times}`,
      onRetry: ({ times, url }) => console.log('retry', times, url),
      onSuccess: ({ url }) => console.log('success', url),
      onError: ({ url }) => console.log('error', url),
    }),
  ],
});
```

## Configuration

### Basic

**retryTimes**
- Type: `number`
- Optional
- Number of retries, default is 3

**retryDelay**
- Type: `number`
- Optional
- Delay between retries in milliseconds, default is 1000

### Advanced

**domains**
- Type: `string[]`
- Optional
- Backup domains for rotation. Default is an empty array

**addQuery**
- Type: `boolean | ((context: { times: number; originalQuery: string }) => string)`
- Optional
- Whether to append a query parameter when retrying, default is false
- If a function is provided, it receives retry count and the original query string, and should return the new query string

**fetchOptions**
- Type: `RequestInit`
- Optional
- Custom fetch options, default is an empty object

**manifestDomains**
- Type: `string[]`
- Optional
- Domain rotation list used when fetching the manifest (e.g. `mf-manifest.json`). Takes precedence over `domains` for manifest fetch retries. Other resources still use `domains`.

### Callbacks

**onRetry**
- Type: `({ times, domains, url, tagName }: { times?: number; domains?: string[]; url?: string; tagName?: string }) => void`
- Optional
- Triggered on each retry
- Params: `times` current retry number, `domains` domain list, `url` request URL, `tagName` resource type

**onSuccess**
- Type: `({ domains, url, tagName }: { domains?: string[]; url?: string; tagName?: string; }) => void`
- Optional
- Triggered when a retry finally succeeds
- Params: `domains` domain list, `url` request URL, `tagName` resource type

**onError**
- Type: `({ domains, url, tagName }: { domains?: string[]; url?: string; tagName?: string; }) => void`
- Optional
- Triggered when all retries fail
- Params: `domains` domain list, `url` request URL, `tagName` resource type

## Details

### Retry logic

The plugin retries automatically when a resource fails to load. The number of retries is controlled by `retryTimes`. For example:

- `retryTimes: 3` means up to 3 retries (after the first attempt)
- A delay of `retryDelay` ms is applied before each retry

### Domain rotation

When `domains` is configured, the plugin rotates the host on each retry:

```javascript
const retryPlugin = RetryPlugin({
  domains: [
    'https://cdn1.example.com',
    'https://cdn2.example.com',
    'https://cdn3.example.com'
  ],
});
```

Order of attempts:
1. Initial attempt: original URL
2. 1st retry: switch to `cdn2.example.com`
3. 2nd retry: switch to `cdn3.example.com`
4. 3rd retry: switch to `cdn1.example.com`

### Cache-busting

Use `addQuery` to add query parameters during retries to avoid cache interference:

```javascript
const retryPlugin = RetryPlugin({
  addQuery: true, // adds ?retryCount=1, ?retryCount=2, etc.
});
```

You can also provide a function:

```javascript
const retryPlugin = RetryPlugin({
  addQuery: ({ times, originalQuery }) => {
    return `${originalQuery}&retry=${times}&timestamp=${Date.now()}`;
  },
});
```

### Callbacks

You can monitor the retry lifecycle with callbacks:

```javascript
const retryPlugin = RetryPlugin({
  onRetry: ({ times, domains, url, tagName }) => {
    console.log(`Retry #${times}, domains: ${domains}, url: ${url}`);
  },
  onSuccess: ({ domains, url, tagName }) => {
    console.log(`Retry success, domains: ${domains}, url: ${url}`);
  },
  onError: ({ domains, url, tagName }) => {
    console.log(`Retry failed, domains: ${domains}, url: ${url}`);
  },
});
```

Callback params:
- `times`: current retry count (starts from 1)
- `domains`: the current domain list
- `url`: current request URL
- `tagName`: resource type ('fetch' or 'script')

## Use cases

### 1. CDN failover

```javascript
const retryPlugin = RetryPlugin({
  retryTimes: 2,
  domains: [
    'https://cdn1.example.com',
    'https://cdn2.example.com',
    'https://cdn3.example.com'
  ],
  addQuery: true,
});
```

### 2. Unstable networks

```javascript
const retryPlugin = RetryPlugin({
  retryTimes: 5,
  retryDelay: 2000,
  onRetry: ({ times }) => {
    console.log(`Unstable network, retry #${times}`);
  },
});
```

### 3. Monitoring and logging

```javascript
const retryPlugin = RetryPlugin({
  onRetry: ({ times, url }) => {
    analytics.track('resource_retry', { times, url });
  },
  onError: ({ url }) => {
    logger.error('Resource load failed after all retries', { url });
  },
});
```

## Notes

1. **Performance**: High retry counts increase loading time; tune for your environment
2. **Domains**: Ensure all domains in `domains` serve the same resource
3. **Caching**: If `addQuery` is enabled, consider CDN caching strategy
4. **Error handling**: After all retries fail, the original error is thrown; handle it upstream

## Error codes

- `RUNTIME_008`: Resource load failure that triggers the retry mechanism
